package com.nulldev.util.scripting.ScriptAPI.plusExt.rhino;

import java.io.Reader;

import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

import com.nulldev.util.JVM.reflections.Reflections;
import com.nulldev.util.data.Variables;

public class RhinoJSE extends AbstractScriptEngine {
	private volatile Object context;
	private volatile Object scriptable;
	private final RhinoJSEFactory factory;
	private final boolean useITL;

	public RhinoJSE(final RhinoJSEFactory fact) {
		this(fact, false);
	}

	public RhinoJSE(final RhinoJSEFactory fact, final boolean enableITL) {
		Variables.requireNonNull(fact, "fact");
		if (!Reflections.is("org.mozilla.javascript.Context"))
			throw new RuntimeException("RhinoJS not found!");
		this.useITL = enableITL;
		final Reflections cxo = new Reflections("org.mozilla.javascript.Context");
		this.context = cxo.staticInvoke("enter");
		final Reflections cxc = Reflections.encapsulate(this.context);
		this.scriptable = cxc.invoke("initStandardObjects");
		this.factory = fact;
	}

	@Override
	public Object eval(final String script, final ScriptContext context) throws ScriptException {
		Variables.requireNonNull(script, "script");
		Variables.requireNonNull(context, "context");
		return Reflections.encapsulate(this.context).invoke("evaluateString", new Class<?>[]
			{ new Reflections("org.mozilla.javascript.Scriptable", false).getClazz(), String.class, String.class, int.class, Object.class }, this.scriptable,
				script, "<cmd>", 1, null);
	}

	@Override
	public Object eval(final Reader reader, ScriptContext context) throws ScriptException {
		Variables.requireNonNull(reader, "script");
		Variables.requireNonNull(context, "context");
		return Reflections.encapsulate(this.context).invoke("evaluateReader", new Class<?>[]
			{ new Reflections("org.mozilla.javascript.Scriptable", false).getClazz(), Reader.class, String.class, int.class, Object.class }, this.scriptable,
				reader, "<cmd>", 1, null);
	}

	@Override
	public Bindings createBindings() {
		return new SimpleBindings();
	}

	@Override
	public ScriptEngineFactory getFactory() {
		return this.factory;
	}

	@Override
	public String toString() {
		return "RhinoJSEFactory[factory=" + this.factory + ",hash=" + this.hashCode() + "]";
	}

	public boolean usingITL() {
		return this.useITL;
	}
}
